<template>
  <div class="chat-interface" v-if="character">
    <div class="chat-container">
      <div class="left-character-section">
        <div class="large-character-blackhole">
          <div class="particles-container" ref="particlesContainer"></div>
          <div class="large-event-horizon"></div>
          <div class="large-blackhole-core">{{ character.initials }}</div>
        </div>
        <h2 class="character-title">{{ character.name }}</h2>
        <p class="character-bio">{{ character.description }}</p>
        <div class="connection-status" :class="{ 'connected': isConnected }">
          {{ connectionStatusText }}
        </div>
      </div>
      <div class="right-chat-section">
        <div class="chat-header">
          <div class="chat-title">与{{ character.name }}对话</div>
          <button class="close-chat-btn" @click="closeChat">×</button>
        </div>
        <div class="chat-messages-container" ref="messagesContainer">
            <div v-for="(msg, index) in messages" :key="index" :class="['message', msg.type]">
                <template v-if="!msg.subType || msg.subType === 'text'">
                    <span v-if="msg.isPartial" :class="{ 'partial-text': msg.isPartial }">{{ msg.text }}</span>
                    <span v-else>{{ msg.text }}</span>
                </template>

                <template v-else-if="msg.subType === 'voice'">
                    <div class="voice-bubble" @click="playVoice(msg)">
                        <svg class="play-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                            <path v-if="!msg.isPlaying" d="M8 5v14l11-7z" fill="currentColor" />
                            <path v-else d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" fill="currentColor" />
                        </svg>
                        <div class="voice-duration">{{ formatDuration(msg.duration) }}</div>
                        <div v-if="msg.isPlaying" class="voice-progress">
                            <div 
                                class="voice-progress-bar" 
                                :style="{ width: msg.progress ? msg.progress * 100 + '%' : '0%' }"
                            ></div>
                        </div>
                    </div>
                </template>
            </div>
            <div v-if="isAiThinking" class="message ai">
                <span class="thinking-dots">思考中<span class="dot">.</span><span class="dot">.</span><span class="dot">.</span></span>
            </div>
        </div>
        <div class="chat-input-section">
          <textarea
            class="chat-text-input"
            v-model="newMessage"
            @keypress.enter.prevent="sendMessage"
            placeholder="输入你的消息..."
            :disabled="!isConnected || isRecording || isAiThinking"
          ></textarea>
          <button 
            class="voice-btn" 
            @click="toggleRecording" 
            :class="{ 'recording': isRecording }"
            :disabled="!isConnected || isAiThinking"
          >
            <svg v-if="!isRecording" class="voice-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
              <path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z" fill="white" />
            </svg>
            <svg v-else class="voice-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
              <path d="M6 6h12v12H6z" fill="white" />
            </svg>
            <span v-if="isRecording" class="recording-time">{{ formatDuration(recordingDuration) }}</span>
          </button>
          <button class="send-btn" @click="sendMessage" :disabled="!isConnected || !newMessage.trim() || isRecording || isAiThinking">
            <svg class="send-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ElMessage } from 'element-plus';

if (typeof window !== 'undefined' && typeof window.global === 'undefined') {
  window.global = window;
}

import { ref, onMounted, onBeforeUnmount, computed, watch, nextTick } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { characters } from '@/data/characters';
import { createParticles, shadeColor } from '@/utils/animations.js';
import { useChatHistory } from '@/composables/useChatHistory';
import { chatVoice } from '@/api/voice'; 

const route = useRoute();
const router = useRouter();
const { saveToHistory, loadHistory } = useChatHistory();

const character = computed(() => characters.find(c => c.initials === route.params.initials));
const messages = ref([]);
const newMessage = ref('');
const messagesContainer = ref(null);
const particlesContainer = ref(null);

const isConnected = ref(false);
const connectionStatusText = ref('连接中...');
let stompClient = null;
const sessionId = computed(() => route.params.sessionId || '49d64044-bb33-470c-a07c-70e4a0c81509');
const personaId = computed(() => route.params.initials);

const isAiThinking = ref(false);

const isRecording = ref(false);
const recordingDuration = ref(0);
const roles_voice = {
    'SG': 'xiaochen',
    'SW': 'jam',
    'DF': 'tongtong'
}
let mediaStream = null;
let recordingTimer = null;
let currentAudio = null;
let progressInterval = null;
let mediaRecorder = null;
let audioChunks = [];

const setCssVariables = (char) => {
    if (!char) return;
    const root = document.documentElement;
    root.style.setProperty('--character-color', char.color);
    root.style.setProperty('--character-color-transparent', `${char.color}60`);
    root.style.setProperty('--character-color-dark', shadeColor(char.color, -30));
    root.style.setProperty('--character-rgb', char.rgb);
};

const connect = async () => {
    try {
        const { Client } = await import('@stomp/stompjs');
        const SockJS = (await import('sockjs-client')).default;
        stompClient = new Client({
            webSocketFactory: () => new SockJS('http://124.222.168.99:8080/ws/chat'),
            debug: (str) => console.log('STOMP Debug: ' + str),
            reconnectDelay: 5000,
        });
        stompClient.onConnect = (frame) => {
            isConnected.value = true;
            connectionStatusText.value = '连接成功';
            stompClient.subscribe(`/topic/chat/${sessionId.value}`, (message) => {
                handleServerMessage(JSON.parse(message.body));
            });
        };
        stompClient.onStompError = (frame) => {
            isConnected.value = false;
            connectionStatusText.value = '连接错误';
            console.error('Broker reported error: ' + frame.headers['message']);
        };
        stompClient.onWebSocketError = (error) => {
            isConnected.value = false;
            connectionStatusText.value = '连接失败';
            console.error('WebSocket Error', error);
        };
        stompClient.onDisconnect = () => {
            isConnected.value = false;
            connectionStatusText.value = '已断开';
        };
        stompClient.activate();
    } catch (error) {
        isConnected.value = false;
        connectionStatusText.value = '加载依赖失败';
        console.error("加载WebSocket模块失败:", error);
    }
};

const disconnect = () => {
    if (stompClient && stompClient.active) {
        stompClient.deactivate();
    }
};

const handleServerMessage = (response) => {
    if (response.type === 'PARTIAL_TEXT' || response.type === 'TEXT') {
        isAiThinking.value = false;
    }
    const lastMessage = messages.value.length > 0 ? messages.value[messages.value.length - 1] : null;
    switch (response.type) {
        case 'TEXT':
            if (lastMessage && lastMessage.type === 'ai' && lastMessage.isPartial) {
                lastMessage.text = response.delta;
                lastMessage.isPartial = false;
            } else {
                messages.value.push({ type: 'ai', text: response.delta, isPartial: false });
            }
            break;
        case 'PARTIAL_TEXT':
            if (lastMessage && lastMessage.type === 'ai' && lastMessage.isPartial) {
                lastMessage.text += response.delta;
            } else {
                messages.value.push({ type: 'ai', text: response.delta, isPartial: true });
            }
            break;
        case 'STATUS':
            if (response.content === 'TTS_COMPLETE' && lastMessage && lastMessage.type === 'ai' && lastMessage.isPartial) {
                lastMessage.isPartial = false;
            }
            break;
        case 'ERROR':
            console.error('服务器错误:', response.delta);
            const errorText = `发生错误: ${response.delta}`;
            if (lastMessage && lastMessage.type === 'ai' && lastMessage.isPartial) {
                lastMessage.text = errorText;
                lastMessage.isPartial = false;
            } else {
                messages.value.push({ type: 'ai', text: errorText });
            }
            isAiThinking.value = false;
            break;
    }
    scrollToBottom();
};

const initializeChat = async () => {
    const history = await loadHistory(character.value.name);
    
    if (history && history.length > 0) {
        history.forEach(msg => {
            if (msg.subType === 'voice' && msg.audioBlob) {
                msg.audioUrl = URL.createObjectURL(msg.audioBlob);
            }
        });
        messages.value = history;
    } else {
        const welcomeMessages = {
            'SG': '我是苏格拉底。真正的智慧在于承认自己的无知。你想探讨什么哲学问题？',
            'SW': '俺老孙来也！有什么妖怪需要我帮你打吗？或者你想听西游故事？',
            'DF': '我是列奥纳多·达·芬奇。艺术与科学本为一体。你想了解什么？',
        };
        messages.value = [{ type: 'ai', text: welcomeMessages[character.value.initials] || `你好！我是${character.value.name}。` }];
    }
    // ✅ 核心修改：在这里调用滚动，保证在数据加载完毕后执行
    scrollToBottom();
};

const scrollToBottom = () => nextTick(() => {
    if (messagesContainer.value) messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
});

const sendMessage = () => {
    if (!isConnected.value) {
        ElMessage.warning('正在连接中，请稍后...');
        return;
    }
    if (isAiThinking.value) {
        ElMessage.warning('AI正在思考中，请稍后再发送');
        return;
    }

    const messageText = newMessage.value.trim();
    if (!messageText || !stompClient) return;
    
    messages.value.push({ type: 'user', text: messageText });
    scrollToBottom();
    const chatMessage = {
      type: "USER_TEXT",
      text: messageText,
      timestamp: parseInt(Date.now() / 1000, 10)
    };
    try {
        isAiThinking.value = true;
        stompClient.publish({
            destination: `/app/chat/${sessionId.value}/text`,
            body: JSON.stringify(chatMessage),
        });
    } catch (error) {
        console.error('[发送失败]', error);
        messages.value.push({ type: 'system', text: '消息发送失败，请重试' });
    }
    newMessage.value = '';
};

const closeChat = async () => {
    if (messages.value.some(m => m.type === 'user')) {
        await saveToHistory(character.value, messages.value);
    }
    router.push('/');
};

const toggleRecording = () => {
  isRecording.value ? stopRecording() : startRecording();
};

const startRecording = async () => {
  if (!isConnected.value) {
      ElMessage.warning('正在连接中，请稍后...');
      return;
  }
  if (isAiThinking.value) {
      ElMessage.warning('AI正在思考中，请稍后再录音');
      return;
  }

  try {
    mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
    mediaRecorder = new MediaRecorder(mediaStream);

    mediaRecorder.ondataavailable = event => {
      audioChunks.push(event.data);
    };

    mediaRecorder.onstop = async () => {
        try {
            const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });

            if (recordingDuration.value < 1) {
                ElMessage.warning('录制时间太短');
                return;
            }

            const audioFile = new File([audioBlob], `user-voice-${Date.now()}.wav`, { type: 'audio/wav' });

            const userVoiceMsg = {
                type: 'user',
                subType: 'voice',
                audioUrl: URL.createObjectURL(audioBlob),
                audioBlob: audioBlob,
                duration: recordingDuration.value,
                timestamp: Date.now(),
                isPlaying: false,
                progress: 0,
            };
            messages.value.push(userVoiceMsg);
            scrollToBottom();
            
            await callChatVoiceApi(audioFile);
        } catch (error) {
            console.error("onstop 处理出错:", error);
        } finally {
            resetRecordingState();
        }
    };

    mediaRecorder.start();
    isRecording.value = true;
    recordingDuration.value = 0;
    recordingTimer = setInterval(() => {
      recordingDuration.value += 1;
      if (recordingDuration.value >= 60) stopRecording();
    }, 1000);

  } catch (error) {
    console.error('MediaRecorder 录制启动失败:', error);
    ElMessage.error('录音启动失败，请检查麦克风权限。');
    resetRecordingState();
  }
};

const stopRecording = () => {
  if (mediaRecorder && mediaRecorder.state === 'recording') {
    mediaRecorder.stop();
    isRecording.value = false;
    if (recordingTimer) {
      clearInterval(recordingTimer);
      recordingTimer = null;
    }
  }
};

const resetRecordingState = () => {
    if (mediaStream) mediaStream.getTracks().forEach(track => track.stop());
    mediaStream = null;
    mediaRecorder = null;
    audioChunks = [];
    recordingDuration.value = 0;
};

const playVoice = (msg) => {
    const messageInArray = messages.value.find(m => m.timestamp === msg.timestamp);
    if (!messageInArray) return;

    if (!messageInArray.audioUrl && messageInArray.audioBlob) {
        messageInArray.audioUrl = URL.createObjectURL(messageInArray.audioBlob);
    }
    
    if (!messageInArray.audioUrl) {
        ElMessage.error('音频文件已损坏，无法播放');
        return;
    }

    if (currentAudio && currentAudio._msgId === messageInArray.timestamp) {
        currentAudio.paused ? currentAudio.play() : currentAudio.pause();
        return;
    }

    if (currentAudio) {
        currentAudio.pause();
    }
    if (progressInterval) {
        clearInterval(progressInterval);
        progressInterval = null;
    }
    messages.value.forEach(m => {
        if (m.isPlaying) {
            m.isPlaying = false;
            m.progress = 0;
        }
    });

    currentAudio = new Audio(messageInArray.audioUrl);
    currentAudio._msgId = messageInArray.timestamp;
    messageInArray.isPlaying = true;

    currentAudio.addEventListener('play', () => {
        messageInArray.isPlaying = true;
        progressInterval = setInterval(() => {
            if (currentAudio && !currentAudio.paused) {
                messageInArray.progress = currentAudio.currentTime / currentAudio.duration;
            }
        }, 100);
    });
    
    currentAudio.addEventListener('pause', () => {
        if (messageInArray) messageInArray.isPlaying = false;
        if (progressInterval) {
            clearInterval(progressInterval);
            progressInterval = null;
        }
    });

    currentAudio.addEventListener('ended', () => {
        if (messageInArray) {
            messageInArray.isPlaying = false;
            messageInArray.progress = 0;
        }
        if (progressInterval) {
            clearInterval(progressInterval);
            progressInterval = null;
        }
        currentAudio = null;
    });

    currentAudio.addEventListener('error', (e) => {
        console.error('音频播放错误:', e);
        ElMessage.error('播放失败，音频源可能已失效或损坏');
        if (messageInArray) {
            messageInArray.isPlaying = false;
            messageInArray.progress = 0;
        }
    });

    currentAudio.play().catch(error => {
        console.error('播放失败 (catch):', error);
        if (messageInArray) {
            messageInArray.isPlaying = false;
            messageInArray.progress = 0;
        }
    });
};

const formatDuration = (seconds) => {
    if (typeof seconds !== 'number' || seconds < 0) return '0s';
    const mins = Math.floor(seconds / 60);
    const secs = Math.round(seconds % 60);
    return mins > 0 
        ? `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}` 
        : `${secs}s`;
};

const callChatVoiceApi = async (audioFile) => {
    try {
        isAiThinking.value = true;
        
        const role_voice = roles_voice[personaId.value];
        const response = await chatVoice(
            audioFile,
            personaId.value,
            sessionId.value,
            'zh-CN',
            role_voice
        );

        const newSessionId = response.headers['x-session-id'];
        const aiAudioBlob = response.data;
        const aiAudioUrl = URL.createObjectURL(aiAudioBlob);

        let aiAudioDuration = 0;
        try {
            const tempCtx = new (window.AudioContext || window.webkitAudioContext)();
            const buffer = await tempCtx.decodeAudioData(await aiAudioBlob.arrayBuffer());
            aiAudioDuration = buffer.duration;
            await tempCtx.close();
        } catch (e) { console.error('计算AI音频时长失败:', e); }

        if (newSessionId && newSessionId !== sessionId.value) {
            router.replace({ params: { ...route.params, sessionId: newSessionId } });
        }

        const aiMsg = {
            type: 'ai',
            subType: 'voice',
            audioUrl: aiAudioUrl,
            audioBlob: aiAudioBlob,
            duration: aiAudioDuration,
            timestamp: Date.now(),
            isPlaying: false,
            progress: 0,
        };
        messages.value.push(aiMsg);
        playVoice(aiMsg);
    } catch (error) {
        console.error('chatVoice接口调用失败:', error);
        const errorMsg = error.response?.status === 500
            ? '服务器处理语音失败'
            : '语音对话请求失败，请检查网络';
        messages.value.push({ type: 'ai', text: errorMsg });
    } finally {
        isAiThinking.value = false;
        scrollToBottom();
    }
};

watch(character, (newChar) => {
    if (newChar) {
        setCssVariables(newChar);
        initializeChat();
        nextTick(() => {
            if (particlesContainer.value) createParticles(particlesContainer.value, newChar.color);
        });
    }
}, { immediate: true });

onMounted(() => {
    document.body.style.overflow = 'hidden';
    connect();
});

onBeforeUnmount(() => {
    document.body.style.overflow = 'auto';
    disconnect();
    resetRecordingState();
    if (currentAudio) currentAudio.pause();
    if (progressInterval) clearInterval(progressInterval);
    messages.value.forEach(msg => {
        if (msg.subType === 'voice' && msg.audioUrl && msg.audioUrl.startsWith('blob:')) {
            URL.revokeObjectURL(msg.audioUrl);
        }
    });
});
</script>

<style scoped>
.chat-interface {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(5, 5, 5, 0.95);
    z-index: 1000;
    padding: 2rem;
    overflow: hidden;
}

.voice-btn {
    background: linear-gradient(45deg, #7bed9f, #2ed573);
}

.voice-btn.calling {
    background: linear-gradient(45deg, #ff4757, #ff3742);
}

.connection-status {
  margin-top: 1rem;
  padding: 0.5rem;
  color: #f0932b;
  text-align: center;
}

.connection-status.connected {
  color: #2ed573;
}

.message .partial-text::after {
  animation: blink 1s step-end infinite;
  margin-left: 2px;
  font-weight: 300;
}

.thinking-dots {
  display: inline-block;
  position: relative;
}

.thinking-dots .dot {
  opacity: 0;
  margin-left: 1px;
  animation: dot-animation 1.5s infinite;
}

.thinking-dots .dot:nth-child(1) { animation-delay: 0.0s; }
.thinking-dots .dot:nth-child(2) { animation-delay: 0.5s; }
.thinking-dots .dot:nth-child(3) { animation-delay: 1.0s; }

@keyframes dot-animation {
  0% { opacity: 0; }
  50% { opacity: 1; }
  100% { opacity: 0; }
}

.voice-btn {
  background: linear-gradient(45deg, #7bed9f, #2ed573);
  border: none;
  border-radius: 50%;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  position: relative;
  transition: all 0.3s ease;
}

.voice-btn:disabled {
  background: #555;
  cursor: not-allowed;
  animation: none;
  box-shadow: none;
}

.voice-btn.recording {
  background: linear-gradient(45deg, #ff4757, #ff3742);
  animation: pulse 1.2s infinite alternate;
}

@keyframes pulse {
  from { box-shadow: 0 0 0 0 rgba(255, 71, 87, 0.6); }
  to { box-shadow: 0 0 0 8px rgba(255, 71, 87, 0); }
}

.voice-icon {
  width: 22px;
  height: 22px;
}

.recording-time {
  position: absolute;
  bottom: -22px;
  left: 50%;
  transform: translateX(-50%);
  color: white;
  font-size: 12px;
  background: rgba(0, 0, 0, 0.7);
  padding: 2px 6px;
  border-radius: 4px;
}

.message {
  margin: 8px 0;
  padding: 12px;
  border-radius: 8px;
  max-width: 70%;
  word-wrap: break-word;
  position: relative;
}

.message.user {
  margin-left: auto;
  background-color: var(--character-color);
  color: white;
}

.message.ai {
  margin-right: auto;
  background-color: #333;
  color: white;
}

.voice-bubble {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 12px;
  border-radius: 16px;
  cursor: pointer;
  transition: background-color 0.2s ease;
  overflow: hidden; 
}

.voice-bubble:hover {
  background-color: rgba(255, 255, 255, 0.1);
}

.play-icon {
  width: 20px;
  height: 20px;
  fill: currentColor;
  flex-shrink: 0;
}

.voice-duration {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.8);
  flex-shrink: 0;
}

.voice-progress {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background-color: rgba(255, 255, 255, 0.2);
  border-radius: 0 0 8px 8px;
}

.voice-progress-bar {
  height: 100%;
  background-color: white;
  border-radius: inherit;
  transition: width 0.1s linear;
}

.chat-input-section {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
}

.chat-text-input {
  flex: 1;
  border-radius: 24px;
  border: none;
  background-color: #333;
  color: white;
  font-size: 14px;
  resize: none;
  padding: 12px 18px;
  height: 48px;
  line-height: 24px;
}

.chat-text-input:disabled {
  background-color: #555;
  cursor: not-allowed;
}


.send-btn {
  background: linear-gradient(45deg, var(--character-color), var(--character-color-dark));
  border: none;
  border-radius: 50%;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
}

.send-btn:disabled {
  background: #555;
  cursor: not-allowed;
}

.send-icon {
  width: 20px;
  height: 20px;
  fill: white;
}

.message.ai .voice-bubble {
  background-color: #333;
}
.message.ai .voice-bubble:hover {
  background-color: #444;
}
</style>